#!/usr/bin/python2.4
#
# Copyright 2011 Google Inc. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


"""Model of a simple dynamic/editable layer.

The layer model represents both the layer's manifest and data.  The model is
used to allow users to create their own simple layers by entering data into
a subset of fields supported by the full layer model.

  Simple: A database model that defines a layer's manifest including
          actual data.
"""

__author__ = 'jasonstredwick@google.com (Jason Stredwick)'


# Disable 'Import not at top of file' lint error.
# pylint: disable-msg=C6204
try:
  import auto_import_fixer
except ImportError:
  pass  # This will fail on unittest, ok to pass.

from layer.models.layer_base import Base
from layer.models.layer_base import GetId

# Disable 'Import not at top of file' lint error.
# pylint: disable-msg=C6204
try:
  from google.appengine.ext import db
except ImportError:
  # For unit testing.
  from google.appengine.ext import db


class Simple(Base):
  """The database model representing a layer's manifest and data.

  The model's key is its name.

  Fields:
    background_js: A text property containing raw js; could be empty.
    content_css: A text property containing raw css; could be empty.
    content_js: A text property containing raw js; could be empty.
    ondemand: A string specifying if the layer should ondemand immediately or
        on-demand.  Values are 'off', 'on, 'fixed'.
    name: A string containing the name of the layer.
    state: A string specifying the layer's starting state.  Values are
        'on', 'off', 'fixed'.
  """
  background_js = db.TextProperty(required=False, default='')
  content_css = db.TextProperty(required=False, default='')
  content_js = db.TextProperty(required=False, default='')
  ondemand = db.StringProperty(required=False, default='off')
  name = db.StringProperty(required=False, default='')
  state = db.StringProperty(required=False, default='off')

  def ToData(self):
    """Returns a dictionary of all the data necessary to edit the layer.

    Returns:
      Dictionary of all the necessary data.
    """
    return {
        'backgroundJs': self.background_js,
        'contentCss': self.content_css,
        'contentJs': self.content_js,
        'ondemand': self.ondemand,
        'name': self.name,
        'state': self.state
    }

  def ToDict(self):
    """Converts model into a dictionary.

    Overrides the Base ToDict function adding in extra information.

    Returns:
      Dict containing the model data.
    """
    data = Base.ToDict(self)
    data['name'] = self.name
    return data

  def ToManifest(self):
    """Create a layer manifest for this layer.

    Returns:
      A string of the layer manifest.
    """
    return {
        'background_scripts': [
            {
                'js_code': [self.background_js]
            }
        ],
        'content_scripts': [
            {
                'css_code': [self.content_css],
                'js_code': [self.content_js],
                'matches': ['*://*/*']
            }
        ],
        'load_immediate': self.ondemand,
        'state': self.state
    }

  def Update(self, background_js, content_css, content_js, ondemand, state):
    """Updates modifiable data.

    Args:
      background_js: A string of raw background_js.
      content_css: A string of raw content_css.
      content_js: A string of raw content_js.
      ondemand: A string representing the on-demand ondemand state of the
                layer.
      state: A string representing the on/off state of the layer.
    """
    self.background_js = background_js or ''
    self.content_css = content_css or ''
    self.content_js = content_js or ''
    self.ondemand = ondemand
    self.state = state


def Create(name, background_js, content_css, content_js, ondemand, state, url):
  """Create a new Simple entry.

  Args:
    name: A string that acts as both the entry's key_name and layer's name.
    background_js: A string containing raw js code.
    content_css: A string containing raw css code.
    content_js: A string containing raw js code.
    ondemand: A string containing the on-demand ondemand state of the layer.
    state: A string containing the on/off state of the layer.
    url: A string containing the url for retrieving the layer's manifest.

  Returns:
    Returns a Simple object that has been added to the data store.
  """
  layer = Simple(key_name=name, name=name, url=url, id=GetId())
  layer.Update(background_js, content_css, content_js, ondemand, state)
  layer.put()
  return layer


def _Get(name):
  """Lookup a simple layer based on name.

  Args:
    name: A string containing the key_name of the layer to retrieve.

  Returns:
    A Simple model object or None if the layer does not exist.
  """
  if not name:
    return None

  layer = Simple.get_by_key_name(name)
  if not layer:
    return None

  return layer


def GetData(name):
  """Returns a dictionary of all the data necessary for the simple editor.

  Args:
    name: A string representing the name of the layer.

  Returns:
    Dictionary of all the necessary data in a default state.
  """
  layer = _Get(name) or Simple(url='invalid_url', id=-1)
  return layer.ToData()


def GetManifest(name):
  """Returns the manifest for the specified layer.

  Args:
    name: A string representing the name of the layer.

  Returns:
    Dictionary of all the necessary data in a default state.
  """
  layer = _Get(name) or Simple(url='invalid_url', id=-1)
  return layer.ToManifest()


def Update(name, serverUrl, background_js, content_css, content_js, ondemand,
           state):
  """Updates modifiable data.

  Args:
    name: A string that acts as both the entry's key_name and layer's name.
    serverUrl: A string representing the base URL of the processing server.
    background_js: A string containing raw js code.
    content_css: A string containing raw css code.
    content_js: A string containing raw js code.
    ondemand: A string containing the on-demand ondemand state of the layer.
    state: A string containing the on/off state of the layer.

  Returns:
    The url or None
  """
  if not name:
    return None

  layer = _Get(name)

  if not layer:
    # TODO(jasonstredwick): Make url a constant.
    url = ''.join([serverUrl, '/layer/simple/get?name=', name])
    layer = Create(name, background_js, content_css, content_js, ondemand,
                   state, url)
  else:
    db.run_in_transaction(_Update, layer, background_js, content_css,
                          content_js, ondemand, state)

  return layer.url


def _Update(layer, background_js, content_css, content_js, ondemand, state):
  """Updates the layer as a transaction.

  Args:
    layer: The layer to be updated.
    background_js: A string containing raw js code.
    content_css: A string containing raw css code.
    content_js: A string containing raw js code.
    ondemand: A string containing the on-demand ondemand state of the layer.
    state: A string containing the on/off state of the layer.
  """
  layer.Update(background_js, content_css, content_js, ondemand, state)
  layer.put()

